package scantask.client.threeDObjects;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import scantask.client.threeDObjects.exceptions.Illegal3DObjectParameter;
import scantask.client.threeDObjects.exceptions.LeafPolygoneException;
import scantask.client.threeDObjects.exceptions.PolygoneCreationException;
import scantask.containers.Coordinate;
import scantask.containers.exceptions.InvalidCoordinateValueException;

public class PolygonePool {

	private final static int INITIAL_POOL_SIZE = 100;

	private final static int MIN_SYNTHETIC_SIZE = 1;
	private final static int MAX_SYNTHETIC_SIZE = 5;

	HashMap<String, ComponentPolygone> _pool;

	/** xml file object **/

	public PolygonePool() {
		this._pool = new HashMap<String, ComponentPolygone>(INITIAL_POOL_SIZE);
	}

	public ComponentPolygone getPolygone(String type) throws PolygoneCreationException {
		if (this._pool.containsKey(type)) {
			return this.clone(this._pool.get(type));
		}
		ComponentPolygone poly = this.read3DObjectFromXMLProtocol(type);
		this._pool.put(type, poly);
		return this.clone(poly);
	}

	public ComponentPolygone getPolygone(String type, int radius) throws PolygoneCreationException{
		String[] typeComponents = type.split("\t");
		if ((typeComponents == null) || (typeComponents.length == 0)) {
			throw new PolygoneCreationException();
		}
		// in case it is pest, the radius already measured at the server
		if ((typeComponents.length >= 2) && (typeComponents[0].compareTo("Measurement") == 0) && (typeComponents[1].compareTo("Pest") == 0)) {
			return this.getPolygone(type + "\t" + radius);
		}
		if (typeComponents[0].compareTo("SyntheticObject") != 0) {
			return this.getPolygone(type);
		}
		if (radius < MIN_SYNTHETIC_SIZE) {
			radius = MIN_SYNTHETIC_SIZE;
		}
		if (radius > MAX_SYNTHETIC_SIZE) {
			radius = MAX_SYNTHETIC_SIZE;
		}
		return this.getPolygone(type + "\t" + radius);
	}

	private ComponentPolygone clone(ComponentPolygone other) {
		return other.clone();
	}




	private ComponentPolygone createSynthetic(Element e,String[] typeComponents) throws Illegal3DObjectParameter, PolygoneCreationException{
		Element radElement = e.getChild("Radius" + typeComponents[2]);
		if (radElement == null) {
			throw new Illegal3DObjectParameter();
		}
		Element synthetic = radElement.getChild(typeComponents[1]);
		if (synthetic == null) {
			throw new Illegal3DObjectParameter();
		}
		ComponentPolygone ans = new CompositePolygone();
		List<Element> components = synthetic.getChildren();
		if (components == null) {
			scantask.client.utilities.logging.SingletonLogger.getLogger().warning("composite object does not contains details in the xml");
			throw new Illegal3DObjectParameter(); 
		}
		Iterator<Element> iter = components.iterator();
		while (iter.hasNext()) {
			Element current = iter.next();
			if (current.getName().compareTo("leaf") == 0) {
				try {
					ans.add(this.createLeaf(current.getChildText("shape"), current.getChildText("color"), 
							current.getChildText("radius"), current.getChildText("Xcoordinate"), current.getChildText("Ycoordinate")
							, current.getChildText("Bottom"), current.getChildText("Up")));
				} catch (LeafPolygoneException e1) {
					//AdirD Cannot reach to this point
				}
			}
			if (current.getName().compareTo("composite") == 0) {
				List<Element> compElement = current.getChildren();
				if (compElement.size() == 1) {
					String xStr = compElement.get(0).getChildText("Xcoordinate");
					String yStr = compElement.get(0).getChildText("Ycoordinate");
					String bottomStr = compElement.get(0).getChildText("Bottom");
					if ((xStr == null) || (yStr == null) || (bottomStr == null)) {
						scantask.client.utilities.logging.SingletonLogger.getLogger().warning("composite object illegal x, y or bottom value in the xml");
						throw new Illegal3DObjectParameter();
					}
					double x = new Double(xStr).doubleValue();
					double y = new Double(yStr).doubleValue();
					double bottom = new Double(bottomStr).doubleValue();
					Coordinate coordinate = null;
					try {
						coordinate = new Coordinate(x, y);
					} catch (InvalidCoordinateValueException e1) {
						scantask.client.utilities.logging.SingletonLogger.getLogger().warning("composite object illegal x or y value in the xml");
						throw new Illegal3DObjectParameter();
					}
					String compName = compElement.get(0).getName();
					int radius = new Integer(typeComponents[2]).intValue();
					ComponentPolygone comp = this.getPolygone(typeComponents[0] + "\t" + compName, radius);
					comp.setLocation(coordinate);
					comp.moveUp(bottom);
					try {
						ans.add(comp);
					} catch (LeafPolygoneException e1) {
						//AdirD Cannot reach to this point
					}
				}
				else {
					scantask.client.utilities.logging.SingletonLogger.getLogger().warning("composite object contains more than one object or zero objects in the xml");
					throw new Illegal3DObjectParameter();
				}
			}
		}
		return ans;
//		if(synthetic.getName().equals("leaf")){
//			return ans;
//		}
//		List<Element> leafs = synthetic.getChildren("leaf");
//		Iterator<Element> iter = leafs.iterator();
//		while(iter.hasNext()){
//			Element toAdd = iter.next();
//			ans.add(createLeaf(toAdd.getChildText("shape"), toAdd.getChildText("color"),
//					toAdd.getChildText("radius"), toAdd.getChildText("Xcoordinate"),
//					toAdd.getChildText("Ycoordinate"), toAdd.getChildText("Bottom"), toAdd.getChildText("Up")));
//		}
//		if(synthetic.getName().equals("SmallOrangeTree")){
//			typeComponents[1] = "SmallOrangeTreeTop";
//			ans.add(createSynthetic(e.getChild("composite"),typeComponents,ans));
//			ans.add(createSynthetic(e.getChild("leaf"),typeComponents,ans));
//		}
//		if(synthetic.getName().equals("pavilion")){
//			ans.add(createSynthetic(e.getChild("leaf"),typeComponents,ans));
//		}
//		//add other kinds of composites
//		throw new Illegal3DObjectParameter();

	}
	
	private ComponentPolygone createLeaf(String shape,String color,
			String radius,String x,String y, String bottom, String up)throws Illegal3DObjectParameter{
		if ((x == null) || (y == null) || (bottom == null) || (up == null) || (shape == null) || (color == null) || (radius == null)) {
			throw new Illegal3DObjectParameter();
		}
		double xd =Double.valueOf(x).doubleValue();
		double yd =Double.valueOf(y).doubleValue();
		double bottomd = Double.valueOf(bottom).doubleValue();
		double upd = Double.valueOf(up).doubleValue();
		double radiusd = Double.valueOf(radius).doubleValue();
		
			if(shape.equals("Box")){
				return new Box(scantask.containers.ColorMap.getColor(color),
						xd, yd, bottomd, upd, 0, radiusd, 0);
			}
			if(shape.equals("Cone")){
				return new Cone(scantask.containers.ColorMap.getColor(color),
						xd, yd, bottomd, upd, 0, (int)radiusd, 0);
			}
			if(shape.equals("Cylinder")){
				return new Cylinder(scantask.containers.ColorMap.getColor(color),
						xd, yd, bottomd, upd, 0, (int)radiusd, 0);
			}
			if(shape.equals("Prism")){
				return new Prism(scantask.containers.ColorMap.getColor(color),
						xd, yd, bottomd, upd, 0, (int)radiusd, 0);
			
			}
			if(shape.equals("Sphere")){
				return new Sphere(scantask.containers.ColorMap.getColor(color),
						xd, yd, bottomd, upd, 0, (int)radiusd, 0);
			
			}
			else throw new Illegal3DObjectParameter();

	}
	private ComponentPolygone createShape(String x, String y, String bottom, String up, String shape,String color,String radius)throws Illegal3DObjectParameter{
		return createLeaf(shape,color,radius,x,y,bottom,up);
	}
	
	
	private ComponentPolygone createMeasurement(Element e,String[] typeComponents)throws Illegal3DObjectParameter{
		Element measurement = e.getChild(typeComponents[1]);
		if(measurement.getName().equals("Pest")){
			if (typeComponents.length != 5) {
				scantask.client.utilities.logging.SingletonLogger.getLogger().warning("Missing parameters to type of Pest");
				throw new Illegal3DObjectParameter();
			}
			String shape = measurement.getChild(typeComponents[3]).getText();
			String color = measurement.getChild(typeComponents[2]).getText();
			return createShape(measurement.getChildText("x"),measurement.getChildText("y"),measurement.getChildText("bottom"),measurement.getChildText("up"),shape,color,typeComponents[typeComponents.length - 1]);	
		}
		else if ((measurement.getName().equals("Temperature")) || (measurement.getName().equals("Moisture"))) {
			return createShape(measurement.getChildText("x"),measurement.getChildText("y"),measurement.getChildText("bottom"),measurement.getChildText("up"),measurement.getChildText("shape"),measurement.getChildText("color"),measurement.getChildText("radius"));
		}
		else throw new Illegal3DObjectParameter();

	}

	private ComponentPolygone read3DObjectFromXMLProtocol(String type) throws PolygoneCreationException{
		
		SAXBuilder builder = new SAXBuilder();
		Element root = null;
		try {
			File xmlFile = new File("lib\\protocol.xml");
			Document doc = builder.build(xmlFile);
			root = doc.getRootElement();
		}

		catch (JDOMException e) {
			scantask.client.utilities.logging.SingletonLogger.getLogger().warning("XML Protocol is not well formed.");
			throw new PolygoneCreationException();
		}
		catch (IOException e) {
			scantask.client.utilities.logging.SingletonLogger.getLogger().warning("XML Protocol IO exception.");
			throw new PolygoneCreationException();
		}
		try {
			String[] typeComponents = type.split("\t");
			if(typeComponents[0].equals("SyntheticObject")){
				return createSynthetic(root.getChild("Synthetic"),typeComponents);

			}
			else if(typeComponents[0].equals("Measurement")){
				return createMeasurement(root.getChild("Measurement"),typeComponents);
			}
			else throw new PolygoneCreationException();
		} catch (Illegal3DObjectParameter e) {
			throw new PolygoneCreationException();
		}
	}

}
